#pragma once

#include <Windows.h>
#include <math.h>

enum STROKE_STATE {
	STROKE_NONE,
	STROKE_START,
	STROKE_MIDDLE,
	STROKE_END
};

class Document
{
private:
	HBITMAP hBmp;
	HDC bufDC;
	static const int gamma[256];

	int _width, _height;
	COLORREF _pc;

	STROKE_STATE stroke;
	
	void *pBmpBits;
	UCHAR *alpha;
	float *alphas;

	int stride;
	
	int *xmin;
	int *xmax;
	
	int ymin, ymax;

	void ResetAlphas() {
		memset( alpha, 255, _width*_height );
		ymin = _height;
		ymax = -1;
		for( int y=0;y<_height;y++ ) {
			xmin[y] = _width;
			xmax[y] = -1;
			for( int x=0; x<_width; x++ )
				alphas[y*_width+x] = 1;
		}
	}
	void PutAlpha( int x, int y, float a ) {
		if( x<0 || y<0 || x>=_width || y>=_height || a==0 ) return;
		if( ymin>y ) ymin = y;
		if( ymax<y ) ymax = y;
		if( xmin[y]>x ) xmin[y] = x;
		if( xmax[y]<x ) xmax[y] = x;

		UCHAR *p = alpha + ( y * _width ) + x;
		*p = (UCHAR)(255.0*a);
		alphas[y*_width+x] = a;
	}
	void RenderAlphas() {
		UCHAR r, g, b;
		for( int y=ymin; y<=ymax; y++ ) {
			UCHAR *p = (UCHAR*)pBmpBits + stride * y + (xmin[y]<<2);
			int yw = y * _width;
			for( int x=xmin[y]; x<=xmax[y]; x++ ) {
				float a = alphas[yw+x];//alpha[yw + x]/255.0;
				float bg = 1 - a;
				GetPixel( x, y, r, g, b );
				*p++ = (int)(GetBValue(_pc)*a) + b*bg;
				*p++ = (int)(GetGValue(_pc)*a) + g*bg;
				*p++ = (int)(GetRValue(_pc)*a) + r*bg;
				*p++ = 255;
			}
		}
	}

	double ipart( double x ) { return (int)(x); }
	double round( double x ) { return ipart( x+0.5 ); }
	double fpart( double x ) { return x - ipart(x); }
	double rfpart( double x ) { return 1 - fpart(x); }
	void swap( double &a, double &b ) { double c = a; a = b; b = c; }
	void DrawLine( double x1, double y1, double x2, double y2 );

public:
	Document() : stroke( STROKE_NONE ), _pc( 0 ), stride(0) {};
	~Document(void);

	int Create( int width, int height );

	void Render( HDC hdc, int x, int y ) { BitBlt( hdc, x, y, _width, _height, bufDC, 0, 0, SRCCOPY); }
	void DrawLine( int x1, int y1, int x2, int y2 );
	void DrawLine( int x1, int y1, int x2, int y2, double width1, double width2 );
	void BeginStroke( int x, int y );
	void EndStroke();
	void DiscardLine() { stroke = STROKE_MIDDLE; }

	COLORREF getPenColorRef() { return _pc; }
	void setPenColor( COLORREF c ) { _pc = c; }

	int getWidth() { return _width; }
	int getHeight() { return _height; }

	void PutPixel( int x, int y ) {
		PutPixel( x, y, 1, 0 );
	}
	
	void PutPixel( int x, int y, double a ) {
		PutPixel( x, y, a, 1-a );
	}

	void PutPixel( int x, int y, double a, double b ) {
		if( x<0 || y<0 || x>=_width || y>=_height ) return;
		/*
		UCHAR *p = (UCHAR*)pBmpBits + (stride * y + (x<<2)/* multi by 4 /);
		UCHAR bg = gamma[(int)(255*b)];
		*p++ = gamma[(int)(GetBValue(_pc)*a)] + bg;
		*p++ = gamma[(int)(GetGValue(_pc)*a)] + bg;
		*p++ = gamma[(int)(GetRValue(_pc)*a)] + bg;
		*p = 255;
		*/
		UCHAR *p2 = (UCHAR*)pBmpBits + (stride * y + ((x+10)<<2)/* multi by 4 */);
		UCHAR bg = (int)(255*b);
		*p2++ = (int)(GetBValue(_pc)*a) + bg;
		*p2++ = (int)(GetGValue(_pc)*a) + bg;
		*p2++ = (int)(GetRValue(_pc)*a) + bg;
		*p2 = 255;
	}

	void GetPixel( int x, int y, UCHAR &r, UCHAR &g, UCHAR &b ) {
		if( x<0 || y<0 || x>=_width || y>=_height ) return;
		UCHAR *p = (UCHAR*)pBmpBits + (stride * y + (x<<2)/* multi by 4 */);
		b = *p++; g = *p++; r = *p;
	}

	void FloodFill( COLORREF c ) {
		int cnt = _width * _height;
		UCHAR r = GetRValue( c );
		UCHAR g = GetGValue( c );
		UCHAR b = GetBValue( c );
		UCHAR *p = (UCHAR*)pBmpBits;

		while( cnt-- >0 ) {
			*p++ = b;
			*p++ = g;
			*p++ = r;
			*p++ = 255; // alpha
		}

		//DrawLine( 100, 100, 115, 250, 53, 40 );
		//DrawLine( 190, 170, 55, 150, 22, 40 );
		//DrawLine( 200, 100, 175, 250, 53, 40 );
		//DrawLine( 115, 250, 90, 400, 40, 10 );
	}
};

